import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.Vector;

import javax.swing.JFrame;
import javax.swing.Timer;



public class nphys extends JFrame implements MouseMotionListener{

	Rectangle r2 = new Rectangle(230, 190, 30, 30);



	//	|_\
	//	Point p1 = new Point(120,180);
	//	Point p2 = new Point(120,300);
	//	Point p3 = new Point(210,300);


	//	|-/
	//	Point p1 = new Point(120,180);
	//	Point p2 = new Point(210, 180);
	//	Point p3 = new Point(210,300);


		Point p1 = new Point(210,180);
//			Point p2 = new Point(210,300);
		Point p3 = new Point(120,300);



//	Point p1 = new Point((int) (400*Math.random()),(int)(600*Math.random()));
//	//	Point p2 = new Point(210,300);
//	Point p3 = new Point((int) (400*Math.random()),(int)(600*Math.random()));

	//	Point p3 = new Point(210,300);
	////		Point p2 = new Point(210,300);
	//	Point p1 = new Point(120,180);


	public nphys(){



		setTitle("N-PhysTutorial test");
		setSize(400,600);
		setVisible(true);
		setDefaultCloseOperation(EXIT_ON_CLOSE);

		(new Timer(10, new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				repaint();				
			}
		})).start();
		this.addMouseMotionListener(this);
	}


	/** Returns true or false if the given point
	 * coordinates (Qx, Qy) are above or below
	 * the line defined by (p1x, p1y) and (p3x, p3y)
	 * on-screen.
	 * @param p1x
	 * @param p1y
	 * @param p3x
	 * @param p3y
	 * @param qx
	 * @param qy
	 * @return
	 */
	//	public boolean isAboveLine(int p1x,int p1y,int p3x,int p3y,int qx,int qy){
	//		double m = (double)(p3y - p1y)/(double)(p3x- p1x);
	//		
	//		System.out.println("M:"+m);
	//		if( qy - p1y >= m* ( qx-p1x) )
	//			return false;
	//		return true;
	//		
	//	}

	/**
	 * Returns whether or not point Q(x,y) is above or below
	 * the line defined by (p1x, p1y) and (p3x, p3y) in
	 * REAL coordinates (not screen-space, which has flipped
	 * y [y+ is downwards]).
	 */
	public Point findPoint2(int ax,int ay,int bx,int by,int qx,int qy){
		Point pt = new Point();
		//bx should always be the right-most point.
		if(bx < ax){
			//swap p3 and p1...
			System.out.println("swapperz");
			int tx = ax, ty = ay;
			ax = bx; ay = by;
			bx = tx; by = ty;
		}

		double slope = (double)(by-ay)/ (double)(bx-ax);

		if( qy - ay > slope*( qx - ax)){ //above
			pt.y = Math.min(ay, by);

			if( slope>=0){
				pt.x = bx;				
			}else{
				pt.x = ax;

			}
		}else{
			pt.y = Math.max(ay,by);

			if(slope >=0){
				pt.x = ax;
			}else{
				pt.x = bx;
			}
		}

		return pt;

		/*
		 * Watch for the case where a and b are points that are right
		 * above one-another!!!!!! (divide by 0 error in the slope)
		 */

	}

	public void paint(Graphics g){

		
		Point p2 = findPoint2(p1.x, p1.y, p3.x, p3.y, (int)r2.getCenterX(), (int)r2.getCenterY());

		
		

		Point n1vec = new Point((p3.y - p1.y),p1.x - p3.x); //Normal vector

		double proj_a = (n1vec.x*(p2.x - p1.x) + n1vec.y*(p2.y-p1.y))/n1vec.distance(0,0);  //generalized projection for p2 = angle opposite hypot


		System.out.println("proja:"+proj_a);

		//draw the projection
		double theta = Math.atan2( n1vec.y, n1vec.x);


		Point p1cr_vec = new Point((int)r2.getCenterX()-p1.x, (int)r2.getCenterY() - p1.y);

		double rproj = project(p1cr_vec,n1vec);
		System.out.println("ref:"+rproj);

		//Project the p1-center vector onto N1 to find the center of the shape...



		Graphics2D g2 = (Graphics2D)g;

		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

		double s_radius = Math.abs(project(new Point(0,r2.height/2),n1vec)) + Math.abs(project(new Point(r2.width/2,0),n1vec));
		System.out.println("rec1/2shadow:"+s_radius);


		System.out.println( r2.getMinY());
		System.out.println("max:"+r2.getMaxY());

		System.out.println("Overlaps: =====");
		System.out.println("Hypotenal: "+overlap(0, proj_a,rproj-s_radius, rproj+s_radius));
		System.out.println("X-axis:"+overlap( p3.x, p1.x, r2.getMinX(), r2.getMaxX() ));
		System.out.println("Y-axis:"+overlap( p1.y, p3.y, r2.getMinY(), r2.getMaxY()));

		System.out.println("Hypotenal: "+overlap2(0, proj_a,rproj-s_radius, rproj+s_radius));
		System.out.println("X-axis:"+overlap2( p3.x, p1.x, r2.getMinX(), r2.getMaxX() ));
		System.out.println("Y-axis:"+overlap2( p1.y, p3.y, r2.getMinY(), r2.getMaxY()));
		System.out.println("======");


		 int dispindex=-1;
         double[] dispvec = null;
         //If overlap is detected
         if(             overlap( p1.y, p3.y, r2.getMinY(), r2.getMaxY()) &&
                         overlap( p3.x,p1.x,r2.getMinX(),r2.getMaxX())&& 
                         overlap(rproj-s_radius,rproj+s_radius,0,proj_a) ){//then...

                 dispvec = new double[]{
                                 overlap2(0, proj_a,rproj-s_radius, rproj+s_radius),
                                 overlap2( p3.x, p1.x, r2.getMinX(), r2.getMaxX() ),
                                 overlap2( p1.y, p3.y, r2.getMinY(), r2.getMaxY())};

                 
                 System.out.println(Arrays.toString(dispvec));
                 
                 double[] mdv=new double[]{Math.abs(dispvec[0]), Math.abs(dispvec[1]), Math.abs(dispvec[2])};
                 
                 if(mdv[0]< mdv[1]){
                         
                         if( mdv[0] < mdv[2])
                                 dispindex = 0;
                         else
                                 dispindex = 2;
                 }else{
                         if( mdv[1]< mdv[2])
                                 dispindex = 1;
                         else
                                 dispindex = 2;
                 }
                 
         }else{

                 
         }
		//Draw some vectors to make shit pretty
		g2.clearRect(0, 0, 1000, 1000);
		//triangle
		g.setColor(Color.red);
		g.drawLine(p1.x, p1.y, p2.x, p2.y);
		g.setColor(Color.blue);
		g.drawLine(p2.x, p2.y, p3.x, p3.y);

		g.setColor(Color.green);
		g.drawLine(p3.x, p3.y, p1.x, p1.y);

		g2.setColor(Color.lightGray);
		drawLine(g2,p1.x,p1.y,n1vec.x+p1.x,n1vec.y+p1.y);

		g2.setColor(Color.DARK_GRAY);
		drawLine(g2,p1.x,p1.y, proj_a * Math.cos(theta)+p1.x, proj_a* Math.sin(theta)+p1.y);

		g2.draw(r2);

		

		Rectangle r3 = (Rectangle) r2.clone();
		if(dispindex == 0){ //hypotenal
			r3.setLocation((int)(r3.x+dispvec[0]*Math.cos(theta)),(int)( r3.y+dispvec[0]*Math.sin(theta)));
		}else if(dispindex == 1){ //xdisplacement
			r3.setLocation( (int)(r3.x + dispvec[1]), r3.y);
		}else if(dispindex == 2){ //ydisplacement
			r3.setLocation( r3.x, (int)(r3.y + dispvec[2]));
		}

		g2.setColor(Color.LIGHT_GRAY);
		g2.draw(r3);


		g2.setColor(Color.red);
		double rr1 = rproj * Math.cos(theta)+p1.x;
		double rr2 = rproj * Math.sin(theta)+p1.y;
		drawLine(g2,rr1 + s_radius*Math.cos(theta) ,rr2 + s_radius*Math.sin(theta) ,rr1 ,rr2);
		drawLine(g2,rr1 - s_radius*Math.cos(theta) ,rr2 - s_radius*Math.sin(theta) ,rr1 ,rr2);
	}



	public static boolean overlap(double a1, double a2, double b1, double b2){

		double r_ep = Math.max(Math.max(a1,a2),Math.max(b1,b2));
		double l_ep = Math.min(Math.min(a1,a2),Math.min(b1,b2));

		double overlap = Math.abs(r_ep - l_ep) - Math.abs( a2-a1) - Math.abs(b2-b1);

		if(overlap < 0) return true;
		return false;

	}




	//The immovable object's projection needs to be specificed first!!!!!! (as a1,a2)
	public static double overlap2(double a1, double a2, double b1, double b2){

		if(a1>a2){
			double t = a1;
			a1 = a2;
			a2 = t;			
		}
		if(b1>b2){
			double t = b1;
			b1 = b2;
			b2 = t;			
		}

		System.out.println("====== DISPLACEMENT=======");

		System.out.println("Immovable: "+a1+","+a2);
		System.out.println("Free movr: "+b1+","+b2);		


		System.out.println("====END DISPLACEMENT====");

		if(  Math.abs(a1-b2) > Math.abs(a2-b1)){ //displace to the right
			System.out.println("to the right");

			return Math.abs(a2-b1);
		}else{
			System.out.println("to the left");

			return -Math.abs(a1-b2);		
		}


	}


	//Vector projection (v1 onto v2)
	public double project(Point v1,/*onto*/ Point v2){
		int a = ((v1.x*v2.x + v1.y*v2.y));
		return ((double)a)/v2.distance(0, 0);
	}

	public void drawLine(Graphics2D g2, double x, double y, double x1, double y1){
		g2.drawLine( (int)Math.round(x), (int)Math.round(y), (int)Math.round(x1), (int)Math.round(y1));
	}


	public static void main(String[]args){
		new nphys();		
	}

	public void mouseDragged(MouseEvent arg0) {
		// TODO Auto-generated method stub

	}

	public void mouseMoved(MouseEvent arg0) {
		r2.setLocation((int)(arg0.getPoint().x - r2.getWidth()/2),(int)( arg0.getPoint().y - r2.getHeight()/2));

	}

}